package com.seezoon.infrastructure.tcp.codec;

import com.seezoon.infrastructure.error.ErrorCode;
import com.seezoon.infrastructure.exception.ExceptionFactory;
import com.seezoon.infrastructure.tcp.common.MessageConstants;
import com.seezoon.infrastructure.utils.BcdUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.TooLongFrameException;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;

@Slf4j
public class MessageDecoder extends ByteToMessageDecoder {

    private static final int MIN_FRAME_SIZE = 15; // 最小帧长度：起始符(1) + 消息头(12 + 4(可选分包字段)) + 校验码(1) + 结束符(1)
    private static final int MAX_FRAME_SIZE = 1024 * 2; // 最大帧长度 2K

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        if (in.readableBytes() < MIN_FRAME_SIZE) {
            return;
        }
        in.markReaderIndex();
        int readerStartIndex = in.readerIndex();
        // 1.查找起始标识符
        byte magicStart = in.readByte();
        if (magicStart != MessageConstants.MAGIC_0X_7E) {
            log.error("read tcp message magic not match {}!= {}", magicStart, MessageConstants.MAGIC_0X_7E);
            throw ExceptionFactory.bizException(ErrorCode.TCP_START_MAGIC_NOT_MATCH);
        }
        ByteBuf tempBuff = Unpooled.buffer(in.readableBytes());
        // 2.一直读到结束符
        byte b;
        int readed = 0;
        boolean foundEndMagic = false;
        // 异步io不阻塞，一定要判断是否有可读字节
        while (in.isReadable()) {
            if ((b = in.readByte()) != MessageConstants.MAGIC_0X_7E) {
                tempBuff.writeByte(b);
                readed++;
                if (readed >= MAX_FRAME_SIZE) {
                    throw new TooLongFrameException("read tcp message too large");
                }
            } else {
                foundEndMagic = true;
                break;
            }
        }
        // 不够读
        if (!foundEndMagic) {
            in.resetReaderIndex();
            return;
        }
        if (log.isDebugEnabled()) {
            byte[] printBuf = new byte[in.readerIndex() - readerStartIndex];
            in.getBytes(readerStartIndex, printBuf);
            log.debug("tcp message received msg is [{}]", Hex.encodeHexString(printBuf));
        }
        // 3.反转义 消息头 ~ 校验码
        ByteBuf byteBuf = this.unescapeBuffer(tempBuff);
        // 非池化的（可选）
        tempBuff.release();
        // 4.校验
        checksumValidate(byteBuf);
        // 5.转结构体
        JTMessage jtMessage = this.parseMessage(byteBuf);
        out.add(jtMessage);
    }


    /**
     * 反转义处理
     */
    private ByteBuf unescapeBuffer(ByteBuf source) {
        ByteBuf target = Unpooled.buffer(source.readableBytes());
        int len = source.readableBytes();

        for (int i = 0; i < len; i++) {
            byte b = source.getByte(i);
            if (b == MessageConstants.MAGIC_0X_7D && i + 1 < len) {
                byte nextByte = source.getByte(i + 1);
                if (nextByte == MessageConstants.TANS_0X01) {
                    target.writeByte(MessageConstants.MAGIC_0X_7D);
                    i++; // 跳过下一个字节
                } else if (nextByte == MessageConstants.TANS_0X02) {
                    target.writeByte(MessageConstants.MAGIC_0X_7E);
                    i++; // 跳过下一个字节
                } else {
                    target.writeByte(b);
                }
            } else {
                target.writeByte(b);
            }
        }
        return target;
    }

    /**
     * 解析消息
     */
    private JTMessage parseMessage(ByteBuf data) {
        if (data.readableBytes() < 12) {
            log.error("read tcp message header length is less than 12");
            throw ExceptionFactory.bizException(ErrorCode.TCP_MESSAGE_FORMAT_ERROR);
        }
        JTMessage message = new JTMessage();
        // 读取消息头
        message.setMsgId(data.readUnsignedShort());
        message.setMsgProperties(data.readUnsignedShort());
        // 读取终端手机号 (6字节)
        byte[] serialNo = new byte[6];
        data.readBytes(serialNo);
        message.setSerialNo(BcdUtils.bcdToStringTrimLeadingZero(serialNo));

        // 读取消息流水号
        message.setMsgSerialNo(data.readUnsignedShort());

        // 检查是否有分包
        if (message.hasSubPackage()) {
            if (data.readableBytes() < 4) {
                log.error("read tcp message subpackage length is less than 4");
                throw ExceptionFactory.bizException(ErrorCode.TCP_MESSAGE_FORMAT_ERROR);
            }
            message.setPackageTotal(data.readUnsignedShort());
            message.setPackageNo(data.readUnsignedShort());
        }
        // 读取消息体
        int bodyLength = message.getBodyLength();
        if (bodyLength > 0) {
            if (data.readableBytes() < bodyLength) { // +1 for checksum
                log.error("read tcp message body length is less than {}", bodyLength);
                throw ExceptionFactory.bizException(ErrorCode.TCP_MESSAGE_FORMAT_ERROR);
            }
            // 这种方式返回是非池化的，需要手动释放，比较麻烦
            ByteBuf bodyBuf = Unpooled.buffer(bodyLength);
            // ByteBuf bodyBuf = data.readBytes(bodyLength);
            data.readBytes(bodyBuf);
            message.setBody(bodyBuf);
        }
        return message;
    }

    private void checksumValidate(ByteBuf data) {
        byte checksum = data.getByte(0);
        for (int i = 1; i < data.readableBytes() - 1; i++) {
            checksum ^= data.getByte(i);
        }
        if (checksum != data.getByte(data.readableBytes() - 1)) {
            log.error("read tcp message checksum not match {}!= {}", checksum,
                    data.getByte(data.readableBytes() - 1));
            throw ExceptionFactory.bizException(ErrorCode.TCP_START_MAGIC_NOT_MATCH);
        }
    }

}